/*
 * LogManager.java
 *
 * Copyright (C) 2007 Irving Bunton
 * http://code.google.com/p/jlogmicro/source
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
// Expand to define CLDC define
@DCLDCVERS@
// Expand to define DJSR75 define
@DJSR75@
// Expand to define logging define
@DLOGDEF@
//#ifdef DLOGGING
package net.sf.jlogmicro.util.logging;

import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

import net.sf.jlogmicro.util.logging.Level;
import net.sf.jlogmicro.util.logging.Logger;

public class LogManager {

	public final static String LOG_PREFIX = "jlomicro-" ;
	public final static String LOG_SUFFIX = "level";
	public final static String LOG_LEVEL = LOG_PREFIX + LOG_SUFFIX;
	public final static String LOG_HANDLERS = "jlomicro-handlers" ;
	public final static String LOG_FORMATTER = "-formatter" ;
	public final static String LOG_NAME = "-name" ;
	public final static String LOG_APPEND = "-append" ;
	public final static String LOG_MAX_ENTRIES = "-limit" ;
	private final static boolean debug = true; // UNDO
	// UNDO 
	private String name = null;
	//UNDO
	private static Form frmRoot = null;
	final private static Hashtable loggers = new Hashtable();
	private static MIDlet midlet;
	private static LogManager instance;
	final private static Logger root = new Logger("root", null);

	protected LogManager() {
	}

	public boolean addLogger(Logger logger) {
		synchronized(this) {
			if (loggers.containsKey(logger.getName())) {
				return false;
			} else {
				final String name = logger.getName();
				loggers.put(name, logger);
				if (logger.getParent() == null) {
					logger.setParent(root);
					final String slevelKey = LOG_PREFIX + name + "-" + LOG_SUFFIX;
					if (debug) {
						System.out.println("midlet=" + midlet);
						System.out.println("slevelKey=" + slevelKey);
					}
					String slevel = null;
					if (midlet != null) {
						slevel = midlet.getAppProperty(slevelKey);
					} else {
						Exception e = new Exception("Logger before LogManager");
						report("Logger before LogManager", e);
					}
					if (debug) {
						System.out.println("slevel=" + slevel);
					}
					Level clevel = null;
					try {
						if (slevel != null) {
							clevel = Level.parse(slevel);
							if (debug) {
								System.out.println("clevel=" + clevel);
							}
						}
					} catch (Throwable e) {
						report("Invalid log level,msg:  " + slevel, e);
						e.printStackTrace();
					}
					if (clevel != null) {
						logger.setUseParentHandlers(false);
						logger.setLevel(clevel);
					} else {
						logger.setUseParentHandlers(true);
					}
				}
				return true;
			}
		}
	}

	public Logger getLogger(String name) {
		synchronized(this) {
			if (loggers.containsKey(name)) {
				return (Logger)loggers.get(name);
			} else {
				return null;
			}
		}
	}

	public void readConfiguration(MIDlet midlet) {
		synchronized(this) {
			LogManager.midlet = midlet;
			try {
				String slevel = getProperty(midlet, LOG_LEVEL, Level.OFF.getName());
				Level level = null;
				try {
					level = Level.parse(slevel);
				} catch (IllegalArgumentException e) {
					report("Invalid log level,msg:  " + slevel, e);
					level = Level.OFF;
					e.printStackTrace();
				}
				root.setLevel(level);
				String shandlers = getProperty(midlet, LOG_HANDLERS, "ConsoleHandler");
				int pos = 0;
				Vector errs = new Vector();
				if (shandlers != null) {
					do {
						shandlers = shandlers.trim();
						String chandler;
						if ((pos = shandlers.indexOf(" ")) > 0) {
							chandler = shandlers.substring(0, pos).trim();
							shandlers = shandlers.substring(pos + 1).trim();
						} else {
							chandler = shandlers.trim();
							shandlers = "";
						}
						report("chandler=" + chandler, null);
						if (chandler.length() == 0) {
							if (shandlers.length() == 0) {
								break;
							}
							continue;
						}
						Handler handler = null;
						try {
							try {
								handler = (Handler)getObjInstance(chandler,
										((Handler)new FormHandler()).getClass(),
										errs);
							} catch (ClassCastException cle) {
								cle.printStackTrace();
								IllegalArgumentException iae =
									new IllegalArgumentException(
									"Invalid handler not subclass " +
									chandler);
								report(iae.getMessage(), iae);
								errs.addElement(iae);
								continue;
							}
							if ((handler != null) &&
									(handler instanceof BufferedHandler)) {
								BufferedHandler bhandler = (BufferedHandler)handler;
								String slogMaxKey = LOG_PREFIX + chandler +
														LOG_MAX_ENTRIES;
								String smaxEntries = getProperty(midlet, slogMaxKey, "1000");
								if (smaxEntries != null) {
									try {
										int maxEntries = Integer.parseInt(
												smaxEntries);
										bhandler.setMaxEntries(maxEntries);
									} catch (NumberFormatException e) {
										e.printStackTrace();
										 
										IllegalArgumentException iae =
											new IllegalArgumentException(
											"Invalid max entries " + smaxEntries);
										report(iae.getMessage(), iae);
										errs.addElement(iae);
										throw iae;
									}
								}
								String snameKey = LOG_PREFIX + chandler + LOG_NAME;
								String defName = "Log";
								if (handler instanceof RecStoreHandler) {
									defName = RecStoreHandler.REC_STORE_NAME;
								}
								String sname = getProperty(midlet, snameKey, defName);
								bhandler.setName(sname);
								String sappendKey = LOG_PREFIX + chandler + LOG_APPEND;
								String sappend = getProperty(midlet, sappendKey, "true");
								boolean append = sappend.equals("true");
								Object oview = bhandler.openView(append);
								// Save so that the reocord store exists
								bhandler.closeView();
								oview = bhandler.openView(append);
								if (bhandler instanceof FormHandler) {
									if (LogManager.frmRoot == null) {
										frmRoot = (Form)oview;
									}
								}
							}
							root.addHandler(handler);
						} catch (IllegalArgumentException iae) {
							report("IllegalArgumentException with handler.", iae);
						}
						String sformatterKey = LOG_PREFIX + chandler +
							LOG_FORMATTER;
						String sformatter = getProperty(midlet, sformatterKey, "SimpleFormatter");
						Formatter formatter = null;
						if (sformatter != null) {
							try {
								formatter = (Formatter)getObjInstance(sformatter,
										((Formatter)new SimpleFormatter(
										)).getClass(), errs);
								if (formatter != null) {
									handler.setFormatter(formatter);
								}
							} catch (IllegalArgumentException iae) {
								iae.printStackTrace();
							} catch (ClassCastException cle) {
								cle.printStackTrace();
								IllegalArgumentException iae =
									new IllegalArgumentException(
									"Invalid formatter not subclass " +
									sformatter);
								report(iae.getMessage(), iae);
								errs.addElement(iae);
							}
						}

					} while (shandlers.length() > 0);
				}
				root.info("logger initialized()");
				for (Enumeration eErrs = errs.elements();
						eErrs.hasMoreElements();) {
					Exception exc = (Exception)eErrs.nextElement();
					root.severe(exc.getMessage());
				}

			} catch (Throwable e) {
				report("ERROR readConfiguration " + e.getMessage(), e);
				e.printStackTrace();
			}
		}
	}

	/* Write to root form so that we can debug and to stdout. */
	private String getProperty(MIDlet midlet, String key, String defValue) {
		String svalue = midlet.getAppProperty(key);
		report(key + "=" + svalue, null);
		if (svalue == null) {
			svalue = defValue;
			report("Null value for " + key + " using default " + svalue, null);
		}
		return svalue;
	}

	/* Write to root form so that we can debug and to stdout. */
	static private void report(String msg, Throwable e) {
		if (frmRoot != null) {
			frmRoot.append(msg + "\n");
		}
		System.out.println(msg);
		if (e != null) {
			System.err.println("Exception " + e.getClass().getName() + " " +
									   e.getMessage());
			e.printStackTrace();
		}
	}

	private static Object getObjInstance(String sclass, Class expClass,
								         Vector errs)
	throws IllegalArgumentException {
		Object rtnObject = null;
		try {
			Class objClass = Class.forName(sclass);
		} catch (ClassNotFoundException e) {
			try {
				Class objClass = Class.forName(
						"net.sf.jlogmicro.util.logging." + sclass);
				rtnObject = objClass.newInstance();
			} catch (ClassNotFoundException ex) {
				IllegalArgumentException iae = new IllegalArgumentException(
						"Class not found or obfuscated " + sclass);
				report(iae.getMessage(), iae);
				errs.addElement(iae);
			} catch (InstantiationException ex) {
				IllegalArgumentException iae = new IllegalArgumentException(
						"Class cannot be instantiated.  Probably " +
						"does not have constructor without parameters " +
						sclass);
				report(iae.getMessage(), iae);
				errs.addElement(iae);
			} catch (IllegalAccessException ex) {
				IllegalArgumentException iae = new IllegalArgumentException(
						"Class cannot be instantiated.  Probably " +
						"an abstract class or interface " + sclass);
				report(iae.getMessage(), iae);
				errs.addElement(iae);
			}
		}
		if (rtnObject == null) {
			if (sclass.equals("ConsoleHandler")) {
				rtnObject = new ConsoleHandler();
			} else if (sclass.equals("FormHandler")) {
				rtnObject = new FormHandler();
			//#ifdef DJSR75
			} else if (sclass.equals("RecStoreHandler")) {
				rtnObject = new RecStoreHandler();
			//#endif
			} else if (sclass.equals("FileHandler")) {
				rtnObject = new RecStoreHandler();
			} else if (sclass.equals("SimpleFormatter")) {
				rtnObject = new SimpleFormatter();
			} else {
				IllegalArgumentException iae = new IllegalArgumentException(
						"Invalid class name " + sclass);
				report(iae.getMessage(), iae);
				errs.addElement(iae);
				throw iae;
			}
		}
		report("info class,instance " +
				rtnObject.getClass().getName() + "," + rtnObject, null);
		return rtnObject;
	}

	/* Get the instance of the LogManager manager. */
	public synchronized static LogManager getLogManager() {
			if( instance == null ) {
					instance = new LogManager();
			}
			return instance;
	}

	/* Get the messages on startup. */
	static public String[] getStartMsgs() {
		if (frmRoot == null) {
			return null;
		}
		Vector vmsgs = new Vector(frmRoot.size());
		final int fsize = frmRoot.size();
		for (int ic = 0; ic < fsize; ic++) {
			Item citem = frmRoot.get(ic);
			if (citem instanceof StringItem) {
				vmsgs.addElement(((StringItem)citem).getText());
			}
		}
		String [] msgs = new String[vmsgs.size()];
		vmsgs.copyInto(msgs);
		return msgs;
	}

}
//#endif
